/*
 * LinearAdc.hpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   LinearAdc.hpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   5 mar 2012
 */

#ifndef LINEARADC_HPP
#define LINEARADC_HPP


#include <boost/serialization/base_object.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>

#include "AnalogToDigitalConverter.hpp"
#include "Voltage.hpp"
#include "AdcValue.hpp"

#include "SerializationException.hpp"

using boost::lexical_cast;

namespace tltgen {

  class AdcVisitor;
  class LinearAdc : public AnalogToDigitalConverter {
  public:

    /**
     * Constructs a new LinearAdc with a given maximum output value and
     * reference voltage. Voltages from 0 to the reference voltage are mapped
     * linearly to integers from 0 to the maximum output value.
     *
     * @param maxOutput the output value of the ADC when given the reference
     *                  voltage as input
     * @param reference the maximum input voltage of the ADC
     */
    LinearAdc(unsigned int nbBits, Voltage reference);
    virtual ~LinearAdc();

    /**
     * Returns whether a given value is a valid number of bits of precisision
     * for constructing a new LinearAdc.
     *
     * @param  nbBits the value to check
     * @return whether a given value is a valid number of bits of precisision
     *         for constructing a new LinearAdc
     */
    static bool isValidNbBits(unsigned int nbBits);

    /**
     * Returns whether a given voltage is a valid reference voltage to use for
     * constructing a new LinearAdc.
     *
     * @param  reference the voltage to check
     * @return whether a given voltage is a valid reference voltage to use for
     *         constructing a new LinearAdc.
     */
    static bool isValidReferenceVoltage(Voltage reference);

    virtual unsigned int getNbBits() const;

    /**
     * Returns this ADC's reference voltage, which is its maximum input voltage.
     *
     * @return this ADC's reference voltage
     */
    virtual Voltage getReferenceVoltage() const;

    /**
     * Returns this ADC's minimum output value, which is the value it will
     * output when given 0 volt as input.
     *
     * @return this ADC's minimum output value
     */
    virtual AdcValue getMinimumOutputValue() const;

    /**
     * Returns this ADC's maximum output value, which is the value it will
     * output when given the reference voltage as input.
     *
     * @return this ADC's maximum output value
     */
    virtual AdcValue getMaximumOutputValue() const;

    
    /** 
     * @copydoc AnalogToDigitalConverter::isValidOutputValue(int)
     */
    virtual bool isValidOutputValue(AdcValue adcValue) const;
    /** 
     * @copydoc AnalogToDigitalConverter::getInputVoltage()
     */
    virtual Voltage getInputVoltage(AdcValue adcValue) const;

    /** 
     * @copydoc AnalogToDigitalConverter::isValidInputVoltage(Voltage)
     */
    virtual bool isValidInputVoltage(Voltage voltage) const;

    /** 
     * @copydoc AnalogToDigitalConverter::getOutputValue()
     */
    virtual AdcValue getOutputValue(Voltage inputVoltage) const;

    virtual void accept(AdcVisitor* visitor) const;

  private:
    const unsigned int nbBits;
    const Voltage reference;

    friend class boost::serialization::access;
    template<class Archive> void serialize(Archive & ar, unsigned int version)
    {
      if (version != 0) {
        throw tltgen::SerializationException("Unknown version " + 
					     lexical_cast<std::string>(version) + " for LinearAdc.");
      }

      ar & boost::serialization::base_object<AnalogToDigitalConverter>(*this);
    }
  };

}

BOOST_CLASS_VERSION(tltgen::LinearAdc, 0)
BOOST_CLASS_EXPORT_KEY(tltgen::LinearAdc)

namespace boost {
  namespace serialization {
    template<class Archive>
    inline void save_construct_data(Archive &ar, const tltgen::LinearAdc *a, const unsigned int version)
    {
      if (version != 0) {
        throw tltgen::SerializationException("Unknown version " + 
					     lexical_cast<std::string>(version) + " for saving construct data for LinearAdc.");
      }

      unsigned int nbBits = a->getNbBits();
      tltgen::Voltage ref = a->getReferenceVoltage();
      ar << nbBits;
      ar << ref;
    }

    template<class Archive>
    inline void load_construct_data(Archive &ar, tltgen::LinearAdc *a, const unsigned int version)
    {
      if (version != 0) {
        throw tltgen::SerializationException("Unknown version " + 
					     lexical_cast<std::string>(version) + " for loading construct data for LinearAdc.");
      }
      
      unsigned int nbBits;
      tltgen::Voltage ref;
      ar >> nbBits;
      ar >> ref;
      ::new(a) tltgen::LinearAdc(nbBits, ref);
    }
  }
}

#endif //LINEARADC_HPP
